﻿using PineLake.Drmwe.SystemConfig;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace PineLake.Drmwe.Encrypts.AES
{
    /// <summary>
    /// AES加密/解密类.
    /// </summary>
    public class AES : IAES
    {
        private static readonly String DefaultKey = !string.IsNullOrWhiteSpace(AppSettings.AESKey) ? AppSettings.AESKey : "Pine&Lake2308$@*PLSoftMM&AES";  //默认密钥需要长度设置为8位以上
        private static readonly String DefaultIv = !string.IsNullOrWhiteSpace(AppSettings.AES_IV) ? AppSettings.AES_IV : "Pine&Lake2308$@*PLSoftMM_IV$";  //默认IV需要长度设置为8位以上

        /// <summary>
        /// 默认AES加密
        /// </summary>
        /// <param name="EncryptStr">加密字符串</param>
        /// <returns></returns>
        public string Encrypt(string EncryptStr)
        {
            return Encrypt(DefaultKey, DefaultIv, Encoding.UTF8.GetBytes(EncryptStr));
        }

        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="EKey">密钥</param>
        /// <param name="EIV">向量IV</param>
        /// <param name="EncryptBytes">加密字符串数组</param>
        /// <returns></returns>
        public string Encrypt(string EKey, string EIV, byte[] EncryptBytes)
        {
            try
            {
                byte[] key = PasswordToKey(EKey, EIV);
                using (var aes = Aes.Create())
                {
                    aes.Key = key;
                    using (ICryptoTransform encryptor = aes.CreateEncryptor())
                    {
                        byte[] cipherBytes = encryptor.TransformFinalBlock(EncryptBytes, 0, EncryptBytes.Length);
                        byte[] packedBytes = Pack(
                            version: 1,
                            iv: aes.IV,
                            cipherBytes: cipherBytes);
                        return Base64Encode(packedBytes);
                    }
                }
            }
            catch (Exception)
            {
                return "403";
            }
        }
        /// <summary>
        /// 默认AES解密
        /// </summary>
        /// <param name="DncryptStr">默认字符串</param>
        /// <returns></returns>
        public string Decrypt(string DncryptStr)
        {
            try
            {
                return Encoding.UTF8.GetString(Decrypt(DncryptStr, DefaultKey, DefaultIv));
            }
            catch (Exception)
            {
                return "403";
            }
        }
        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="DncryptStr">解密字符串</param>
        /// <param name="EKey">密钥</param>
        /// <param name="EIV">向量IV</param>
        /// <returns></returns>
        public byte[] Decrypt(string DncryptStr, string EKey, string EIV)
        {
            byte[] key = PasswordToKey(EKey, EIV);
            byte[] packedBytes = Base64Decode(DncryptStr);
            (byte version, byte[] iv, byte[] cipherBytes) = Unpack(packedBytes);
            using (var aes = Aes.Create())
            {
                using (ICryptoTransform decryptor = aes.CreateDecryptor(key, iv))
                {
                    return decryptor.TransformFinalBlock(cipherBytes, 0, cipherBytes.Length);
                }
            }
        }
        private static byte[] PasswordToKey(string EKey, string EIV)
        {
            using (var hmac = new HMACMD5(Encoding.UTF8.GetBytes(EIV)))
            {
                return hmac.ComputeHash(Encoding.UTF8.GetBytes(EKey));
            }
        }

        private static string Base64Encode(byte[] bytes)
        {
            return Convert.ToBase64String(bytes)
                    .Replace("/", "_")
                    .Replace("+", "-");
            //.Replace("=", "");
        }

        private static byte[] Base64Decode(string base64Str)
        {
            return Convert.FromBase64String(base64Str
                .Replace("_", "/")
                .Replace("-", "+"));
        }

        private static (byte version, byte[] iv, byte[] cipherBytes) Unpack(byte[] packedBytes)
        {
            if (packedBytes[0] == 1)
            {
                // version 1
                return (1, packedBytes[1..(1 + 16)], packedBytes[(1 + 16)..]);
            }
            else
            {
                throw new NotImplementedException("unknown version");
            }
        }

        private static byte[] Pack(byte version, byte[] iv, byte[] cipherBytes)
        {
            return new[] { version }.Concat(iv).Concat(cipherBytes).ToArray();
        }
    }
}
